package com.lmind.bean;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.beanutils.BeanUtilsBean;
import org.apache.commons.beanutils.PropertyUtils;

@SuppressWarnings({"rawtypes", "unchecked"})
public class ObjectWalker {

	private IObjectVisitor visitor;
	
	private boolean terminate = false;

	public IObjectVisitor getVisitor() {
		return visitor;
	}

	public void setVisitor(IObjectVisitor visitor) {
		this.visitor = visitor;
	}

	public void walk(Object object) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		
		if (object == null)
			return;
		terminate = false;

		if (object instanceof Map) {
			walkMap((Map) object);
			if (terminate)
				return;
		} else if (object instanceof List) {
			walkList((List) object);
			if (terminate)
				return;
		} else if (object.getClass().isArray()) {
			walkArray(object);
			if (terminate)
				return;
		} else {
			walkProperty(object);
			if (terminate)
				return;
		}
	}

	private void walkProperty(Object o) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {
		
		for (PropertyDescriptor desc : BeanUtilsBean.getInstance()
				.getPropertyUtils().getPropertyDescriptors(o)) {
			String name = desc.getName();
			Object value = PropertyUtils.getProperty(o, name);
			
			WalkerContext context = createContext(o, name, value);
			visitor.visitProperty(context);
			
			if (context.isValueChanged()) {
				PropertyUtils.setProperty(o, name, context.getValue());
			}
			if (context.isTerminate()) {
				terminate = true;
				return;
			}
			else if (context.isStop()) {
				continue;
			}
			walk(value);
		}
	}

	private void walkMap(Map map) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {

		for (Object item : map.entrySet()) {
			Entry entry = (Entry) item;
			Object key = entry.getKey();
			Object value = entry.getValue();
			WalkerContext context = createContext(map, key, value);

			visitor.visitMap(context);
			
			if (context.isValueChanged()) {
				entry.setValue(context.getValue());
			}
			if (context.isTerminate()) {
				terminate = true;
				return;
			}
			else if (context.isStop()) {
				continue;
			}
			walk(value);
		}
	}

	private void walkList(List list) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {

		for (int i = 0; i < list.size(); i++) {
			Object value = list.get(i);
			WalkerContext context = createContext(list, i, value);

			visitor.visitList(context);
			
			if (context.isValueChanged()) {
				list.set(i, context.getValue());
			}
			if (context.isTerminate()) {
				terminate = true;
				return;
			}
			else if (context.isStop()) {
				continue;
			}
			walk(value);
		}
	}

	private void walkArray(Object array) throws IllegalAccessException,
			InvocationTargetException, NoSuchMethodException {

		int c = Array.getLength(array);
		for (int i = 0; i < c; i++) {
			Object value = Array.get(array, i);
			WalkerContext context = createContext(array, i, value);

			visitor.visitArray(context);
			
			if (context.isValueChanged()) {
				Array.set(array, i, context.getValue());
			}
			if (context.isTerminate()) {
				terminate = true;
				return;
			}
			else if (context.isStop()) {
				continue;
			}
			walk(value);
		}
	}
	
	private WalkerContext createContext(Object o, String name, Object value) {
		return new WalkerContext(o, name, value, null, 0);
	}
	
	private WalkerContext createContext(Map map, Object key, Object value) {
		return new WalkerContext(map, null, value, key, 0);
	}

	private WalkerContext createContext(Object list, int index, Object value) {
		return new WalkerContext(list, null, value, null, index);
	}

	public static void main(String[] args) {

	}
}
